Skip to content

Support TCP for protocol messages - #3636

Open
softins wants to merge 37 commits into
jamulussoftware:mainfrom
softins:tcp-protocol
Open

Support TCP for protocol messages#3636
softins wants to merge 37 commits into
jamulussoftware:mainfrom
softins:tcp-protocol

Conversation

@softins

@softins softins commented Mar 11, 2026

Copy link
Copy Markdown
Member

Short description of changes

Support fallback to TCP for protocol messages, in order to overcome potential loss of large messages due to UDP fragmentation. Currently an incomplete draft, for comment as development continues.

CHANGELOG: Client/Server: Support TCP fallback for protocol messages.

Context: Fixes an issue?

Discussed in issue #3242.

Does this change need documentation? What needs to be documented and how?

It will need documentation once design and development are complete. Particularly need to explain the firewall requirements for a server or directory.

Status of this Pull Request

Incomplete, still under development. Main server side complete and working. Client side development in progress. Complete and ready for review and testing. Still marked draft as it needs some of the debug messages to be commented out before merging.

What is missing until this pull request can be merged?

A lot of testing of both server and client. Intended for Jamulus 4.0.0.

Checklist

  • I've verified that this Pull Request follows the general code principles
  • I tested my code and it does what I want
  • My code follows the style guide
  • I waited some time after this Pull Request was opened and all GitHub checks completed without errors.
  • I've filled all the content above

@softins softins added this to the Release 4.0.0 milestone Mar 11, 2026
@softins softins self-assigned this Mar 11, 2026
@softins

softins commented Mar 11, 2026

Copy link
Copy Markdown
Member Author

So far, this implements the server side of the design described here and here

@softins
softins force-pushed the tcp-protocol branch 4 times, most recently from 5e1a658 to 0ae51e2 Compare March 16, 2026 13:05
@softins softins linked an issue Mar 16, 2026 that may be closed by this pull request
@softins softins added the feature request Feature request label Mar 16, 2026
@softins
softins force-pushed the tcp-protocol branch 3 times, most recently from 7ad1d1f to d939e5b Compare March 26, 2026 17:38
@softins

softins commented Mar 28, 2026

Copy link
Copy Markdown
Member Author

So the next stage of implementation has been achieved: client-side support in the Connect dialog.

  1. If the server list has not been received via UDP when the associated message indicating TCP support has arrived, the client will retry fetching the server list over TCP.
  2. If the client list for a server has not been received via UDP when the associated message indicating TCP support has arrived, the client will retry fetching the client list over TCP, and will continue to use TCP for that server while the Connect dialog is open.
  3. A directory or server that does not have TCP support will not send the TCP supported message, and will continue to be handled as in current versions.
  4. If the server list or client list is successfully received over UDP, there is no need for the client to try TCP.

It has been tested by using nft to drop outbound Jamulus UDP messages with a specific message ID, to simulate loss due to fragmentation.

Examples for a directory-enabled server running on port 22120:

  • drop UDP server list: nft add rule inet filter output udp sport 22120 @ih,16,16 0xee03 drop
  • drop UDP client list: nft add rule inet filter output udp sport 22120 @ih,16,16 0xf503 drop
  • drop UDP "TCP supported" msg: nft add rule inet filter output udp sport 22120 @ih,16,16 0xfb03 drop

Note that nft rules require network byte order (big-endian), but Jamulus IDs are little-endian:

  • CLM_SERVER_LIST = 1006 = 0x03ee => 0xee03 (LE byte order)
  • CLM_RED_SERVER_LIST = 1018 = 0x03fa => 0xfa03 (LE byte order)
  • CLM_CONN_CLIENTS_LIST = 1013 = 0x03f5 => 0xf503 (LE byte order)
  • CLM_TCP_SUPPORTED = 1019 = 0x03fb => 0xfb03 (LE byte order)

@softins

softins commented Mar 28, 2026

Copy link
Copy Markdown
Member Author

The next step is to try implementing the connected-mode TCP described here

@ann0see
ann0see self-requested a review April 7, 2026 14:51
Comment thread src/tcpserver.h
Comment thread src/main.cpp
bool bUseTranslation = true;
bool bCustomPortNumberGiven = false;
bool bEnableIPv6 = false;
bool bEnableTcp = false;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we'll have a long time for the 4.0 release, I'd enable it by default soon (of course once we've tested that the basics work)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I disagree. It's a server-only option, and most servers operators will not need to enable TCP support. Only those running large directories or large servers will need to, and they also need to understand and configure their firewall requirements.

TCP support in the client will indeed be enabled by default, but will only take effect when talking to a directory or server that has enabled it.

If a server operator enables TCP without having configured their firewall correctly, client users could have problems as the server would advertise TCP support to the client, but the client could be unable to connect.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not give an error message or fallback procedure in case the TCP connection timed out?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'm sure we can. I haven't yet tested that scenario.

But it doesn't negate my view that server-side TCP support needs to be an explicit option.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since when do we have a long time?

There's absolutely no benefit to anyone involved in the project - developers or users - in having long release cycles.

A version cut takes very little time - apart from the translation process. Even then, getting used to more, small changes is likely to speed things up generally.

Comment thread src/connectdlg.cpp Outdated
Comment thread src/connectdlg.cpp Outdated
@ann0see ann0see added the bug Something isn't working label Apr 9, 2026
@github-project-automation github-project-automation Bot moved this to Triage in Tracking Apr 9, 2026
@ann0see ann0see moved this from Triage to In Progress in Tracking Apr 9, 2026
@softins

softins commented Apr 9, 2026

Copy link
Copy Markdown
Member Author

Well I've finished implementing everything I intended to, for directory, server and client, so it's ready for reviewing and trying out, as and when time permits (post 3.12.0).

I have a private directory and server built and running with TCP support, at newjam.softins.co.uk on the standard port 22124.

In order to demonstrate the use of TCP in a new client's connect dialog, it will be necessary to use custom firewall filters on the client end to temporarily drop incoming UDP Jamulus protocol messages containing a server list or connected clients list.

There is full forward and backward compatibility between clients and servers built with TCP support and older versions.

@softins
softins marked this pull request as ready for review April 9, 2026 22:48
@softins
softins marked this pull request as draft April 10, 2026 06:30
@softins

softins commented Apr 10, 2026

Copy link
Copy Markdown
Member Author

Keeping as draft, because it will need quite a few debug messages removed before merging.

- Use better randomn number generator
- Only include TCP in server features if a listener is running
  (which it always should be)
This is so that CClient::OnSendCLProtMessage can identify the correct
pendingList hash to use when handling TCP connect errors.

The ID is unused by CServer and CTestBench, so is not named.
@mcfnord

mcfnord commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

(I'm experimenting with asking AI to "roast" errors it sees in other AI output, and it's only fair to "roast" itself when it discovers an error in its own previous work. I hope some humor might help us pay attention to the concerning AI predilection toward confident error. Perhaps this escalates into a Battle of Poindexters, but so far it seems sensible.)

🤖 AI: Correction to the review's last line: FS_TCP_ENABLED is read. OnCLReqServerFeatures sets the bit and sends it in CLM_SERVER_FEATURES on request; CodeRabbit's wording was narrower, "no current source code reads" it. The body now says "set for CLM_SERVER_FEATURES and tested nowhere in src/". The same review measured a 3211-byte welcome message and missed a 4-byte bitmask.

@ann0see

ann0see commented Sep 11, 2026

Copy link
Copy Markdown
Member

🤖 AI: independent functional verification at head 05aaf0a1

Built the headless server (-s -n --enabletcp) and the GUI-less client on Linux (Qt 5.15) and drove them with a small Python harness implementing the framing independently (little-endian 7-byte header, CRC over header+data) plus one real Jamulus client (jackd dummy). All results below were observed against the current head; evidence reproducible via port 22140/22160/22170 runs.

  • --enabletcp opens IPv4/IPv6 TCP listeners on the server port; without it, no TCP listener exists at all.
  • Connected-mode offer: a UDP packet failing frame parse creates a channel, then the server sends CLM_TCP_OFFERED (1023, body = 2-byte message ID + 4-byte token; observed id 1024, token 0xF634D2AC) and CLIENT_ID (32) over UDP.
  • Session auth: CLM_CLIENT_ID (1024) with the correct chanID+token over TCP links the session; a wrong token, unknown chanID (249), or a different channel's token is rejected by closing the TCP connection.
  • After auth, CLM_REQ_CONN_CLIENTS_LIST (1014) sent over the session TCP is answered over TCP (1013).
  • List push: when another client updates its channel info (CHANNEL_INFOS 25 via UDP), the authenticated session receives an unsolicited updated 1013 over TCP (46 bytes = 16-byte entry for the empty-info peer + 30-byte entry carrying name/city). Updated client lists do reach connected clients over the long TCP session as designed.
  • MTU list: server -u 122 with 120 mock clients kept alive by UDP pokes returned a 1920-byte 1013 over TCP (fragmented and reassembled correctly).

  • Timers: unauthenticated TCP closed after ~5 s; a session without keepalives closed after ~18.7 s idle; the client keepalive (15 s interval) kept a real-client session alive >45 s.
  • Real client: Jamulus-client -c 127.0.0.1:22140 -j -n established and silently authenticated its TCP session (no server-side warnings) and stayed connected throughout.

Not exercised here: directory-mode server-list fetch (no directory instance available), and injected UDP loss (no root for tc/netem/nft), so the UDP→TCP fallback paths are code-traced rather than drop-tested in this run.

🤖 Used AI: opencode (big-pickle)

@ann0see

ann0see commented Sep 11, 2026

Copy link
Copy Markdown
Member

Unrelated: (The AI read COMMENTING.md as per AGENTS.md - so I think we can and should indeed go forward with the restructuring of the docs)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working feature request Feature request needs documentation PRs requiring documentation changes or additions

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

Support TCP for protocol messages

5 participants